#!/bin/env ruby

# Copyright 2024 Henrique Paone
#
# This file is part of Tanki.
#
# Tanki is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
# 
# Tanki is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along with
# Tanki. If not, see <https://www.gnu.org/licenses/>.

require 'optparse'
require 'io/console'
require_relative 'lib/card.rb'

include Error
include Tui

Root = "#{ Dir.home }/.tanki"
Options = { 
  ignore_err: false,
  decks:      false,
  cards:      false 
}

def print_help
  puts <<~END
    Usage: tanki [-i] [-c CARD1, CARD2...] [-d DECK1, DECK2...]
    Flash-card terminal program

    -i  Ignore cards that can't be parsed
    -c  Specify the cards you want to see
    -d  Specify the decks you want to see
  END
end

# Grabs arguments up until passed string
def fetch_args_until(str)
  args = []
  until ARGV.empty?
    arg = ARGV.shift
    if arg == str
      ARGV.unshift(arg)
      break 
    end
    args << arg
  end
  args
end

trap('INT') do
  puts "Caught SIGINT, exiting..."
  exit
end
trap('TERM') do
  puts "Caught SIGTERM, exiting..."
  exit
end
at_exit do 
  show_cursor
  restore_screen
end

decks = cards = []

until ARGV.empty?
  arg = ARGV.shift
  case arg
  when '-i'
    Options[:ignore_err] = true
  when '-d'
    Options[:decks] = true
    decks = fetch_args_until('-c')
  when '-c'
    Options[:cards] = true
    cards = fetch_args_until('-d')
  when '-h', '--help'
    print_help
    exit
  else
    raise BadArgument, Err_msgs[:unknown_arg] % { arg: arg }
  end
end

raise BadArgument, Err_msgs[:missing_deck ] if Options[:decks] and decks.empty?
raise BadArgument, Err_msgs[:missing_card ] if Options[:cards] and cards.empty?

glob = []
if Options[:decks] or Options[:cards] 
  decks.each do |deck|
    glob << "**/#{ deck }/**/*.card"
  end
  cards.each do |card|
    glob << "**/#{ card }"
  end
else 
  glob << '**/*'
end

Dir.mkdir(Root) unless Dir.exist?(Root)
Dir.chdir(Root)
 
cards = Dir.glob(glob, base: Root).uniq.select do |file| not Dir.exist?(file) end

raise BadArgument, Err_msgs[:no_cards] if (Options[:cards] or Options[:decks]) and cards.empty?

save_screen
hide_cursor

cards = cards.map do |c|
  begin
    Card.new(c)
  rescue *[ParsingError, BadValue]
    next if Options[:ignore_err]
    raise $!
  ensure
    restore_cursor
    restore_screen
  end
end.compact.select do |c| c.pending? end

loop do
  wrong = []
  amt = cards.size
  cards.shuffle.each do |card|
    in_prompt = true
    trap('WINCH') do
      if in_prompt
        card.show_prompt(amt)
      else
        card.show_answer(amt)
      end
    end
    
    card.show_prompt(amt)
    loop do |c| 
      c = readc
      break if c == "\r" 
      exit if c == 'q'
    end
    in_prompt = false
    card.show_answer(amt)
    
    loop do
      case readc
      when 'r'
        card.incr
        amt -= 1
        break
      when 'w'
        card.reset
        wrong << card
        break
      when 'q'
        exit
      end
    end
    card.last_review = Date.today
    card.write
  end
  break if wrong.empty?
  cards = wrong
end
